(define pe->lex-pe
  (lambda (e)
    (letrec ((helper 
              (lambda (e params env)
                (cond 
                  ((null? e) e)
                  ((starts-with e 'const)
                   e)
                  ((starts-with e 'var)
                   (if (param? (cadr e) params)
                       `(param-var ,(cadr e) ,(find-minor (cadr e) params))
                       (let ((major-minor (find-major-minor (cadr e) env)))
                         (if major-minor
                             `(bound-var ,(cadr e) ,(car major-minor) ,(cdr major-minor))
                             `(free-var ,(cadr e))))))
                  ((starts-with e 'if-3)
                   (let ((test (cadr e))
                         (dit  (caddr e))
                         (dif  (cadddr e)))
                     `(if-3 ,(helper test params env)
                            ,(helper dit  params env)
                            ,(helper dif  params env))))
                  ((starts-with e 'lambda-simple)
                   `(lambda-simple ,(cadr e) ,(helper (caddr e) (cadr e) (cons params env))))
                  ((starts-with e 'lambda-variadic)
                   `(lambda-variadic ,(cadr e) ,(helper (caddr e) (list (cadr e)) (cons params env))))
                  ((starts-with e 'lambda-opt)
                   `(lambda-opt ,(cadr e) ,(caddr e) ,(helper (cadddr e) `(,@(cadr e) ,(caddr e))
                                                                  (cons params env))))
                  ((starts-with e 'set)
                   `(set ,(helper (cadr e) params env) ,(helper (caddr e) params env)))
                  ((starts-with e 'define)
                   `(define ,(helper (cadr e) params env) ,(helper (caddr e) params env)))
                  ((or (starts-with e 'applic) (starts-with e 'tc-applic))
                   `(,(car e) ,(helper (cadr e) params env) ,(helper (caddr e) params env)))
                  ((starts-with e 'seq)
                   `(seq ,(helper (cadr e) params env)))
                  ((starts-with e 'or)
                   `(or ,(helper (cadr e) params env)))
                  ((pair? e)
                   `(,(helper (car e) params env) ,@(helper (cdr e) params env)))
                  (else
                   (error "unknown expression" e))))))
      (helper e () ()))))

(define find-minor
  (lambda (name lst)
    (letrec ((helper
              (lambda (name index lst)
                (if (null? lst)
                    #f
                    (if (equal? name (car lst))
                        index
                        (helper name (+ 1 index) (cdr lst)))))))
    (helper name 0 lst))))

(define find-major-minor
  (lambda (name env)
    (letrec ((helper
              (lambda (name env major) 
                (if (null? env)
                    #f
                    (let ((minor (find-minor name (car env))))
                      (if (not minor)
                          (helper name (cdr env) (+ major 1))
                          (cons major minor)))))))
      (helper name env 0))))
    
(define param?
  (lambda (name params)
    (member name params))) 
                  


(define lambda-params
  (lambda (e)
    (cond ((eq? 'lambda-simple   (car e)) (cadr e))
          ((eq? 'lambda-opt      (car e)) (cons (caddr e) (cadr e)))
          ((eq? 'lambda-variadic (car e)) (list (cadr e))))))

(define lambda-body
  (lambda (e)
    (cond ((eq? 'lambda-simple   (car e)) (caddr e))
          ((eq? 'lambda-opt      (car e)) (cadddr e))
          ((eq? 'lambda-variadic (car e)) (caddr e)))))

(define starts-with
  (lambda (e word)
    (if (eq? word 'lambda)
        (member (car e) '(lambda-simple lambda-variadic lambda-opt))
        (eq? (car e) word))))